/*
 * Copyright 2007. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the GNU Lesser General Public License, Version 2. You
 * can find a copy of the license at:
 * 
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */
package com.sinai.mshab.client.ui.basic;

import java.util.Date;
import java.util.Iterator;

import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;
import com.sinai.mshab.client.util.Answer;
import com.sinai.mshab.client.ui.aggregate.DataReceiver;
import com.sinai.mshab.client.ui.aggregate.FormData;

/**
 * This widget provides 3 drop downs, one for year, month and day Sometimes it
 * is more convinient to use this over date picker.
 * 
 * @author serge
 * 
 */
public class DropDownDateComponent extends QuestionComponent {

	/**
	 * The default number of years ahead of the current year.
	 */
	public static final int YEARS_AHEAD_CURRENT = 50;

	/**
	 * The default number of years before the current year.
	 */
	public static final int YEARS_BEFORE_CURRENT = 100;

	/** We need access to our widgets */
	private ListBox year, month, day;

	/**
	 * The current year.
	 */
	int current_year;

	/**
	 * The months of the year.
	 */
	private String[] months = { "January", "February", "March", "April", "May",
			"June", "July", "August", "September", "October", "November",
			"December" };

	/**
	 * Creates drop down boxes to select a date from. The default alignment is:<br />
	 * 
	 * description | widget
	 * 
	 * @param dbfieldName
	 *            the name of the field in the database that stores the info of
	 *            this widget.
	 * @param tableName
	 *            the name of the database table where dbfieldName lives.
	 * @param description
	 *            the description label of this widget.
	 */
	public DropDownDateComponent(String dbfieldName, String tableName,
			String description) {
		initializeWidget(dbfieldName, tableName, description, null, null, true);
	}

	/**
	 * Creates drop down boxes to select a date from. The horizontal flag will
	 * dictate how the underlying components will be arranged. If horizontal is
	 * true, the arrangement is as follows:
	 * 
	 * description | widget | quantifier | error_msg
	 * 
	 * If horizontal is false, the arrangement is as follows:
	 * 
	 * description <br />
	 * widget <br />
	 * quantifier <br />
	 * error_msg
	 * 
	 * @param dbfieldName
	 *            the name of the field in the database that stores the info of
	 *            this widget.
	 * @param tableName
	 *            the name of the database table where dbfieldName lives.
	 * @param description
	 *            the description label of this widget.
	 * @param horizontal
	 *            flag to indicate whether horizontal or vertical.
	 */
	public DropDownDateComponent(String dbfieldName, String tableName,
			String description, boolean horizontal) {
		initializeWidget(dbfieldName, tableName, description, null, null,
				horizontal);
	}

	/**
	 * Creates drop down boxes to select a date from. The default alignment is:<br />
	 * 
	 * description | widget | quantifier
	 * 
	 * @param dbfieldName
	 *            the name of the field in the database that stores the info of
	 *            this widget
	 * @param tableName
	 *            the name of the database table where dbfieldName lives.
	 * @param description
	 *            the description label of this widget.
	 * @param quantifier
	 *            the quantifier label of this widget.
	 */
	public DropDownDateComponent(String dbfieldName, String tableName,
			String description, String quantifier) {
		initializeWidget(dbfieldName, tableName, description, quantifier, null,
				true);
	}

	/**
	 * Creates drop down boxes to select a date from. The horizontal flag will
	 * dictate how the underlying components will be arranged. If horizontal is
	 * true, the arrangement is as follows:
	 * 
	 * description | widget | quantifier | error_msg
	 * 
	 * If horizontal is false, the arrangement is as follows:
	 * 
	 * description <br />
	 * widget <br />
	 * quantifier <br />
	 * error_msg
	 * 
	 * @param dbfieldName
	 *            the name of the field in the database that stores the info of
	 *            this widget.
	 * @param tableName
	 *            the name of the database table where dbfieldName lives.
	 * @param description
	 *            the description label of this widget.
	 * @param quantifier
	 *            the quantifier label of this widget.
	 * @param horizontal
	 *            flag to indicate whether horizontal or vertical.
	 */
	public DropDownDateComponent(String dbfieldName, String tableName,
			String description, String quantifier, boolean horizontal) {
		initializeWidget(dbfieldName, tableName, description, quantifier, null,
				true);
	}

	/**
	 * Creates drop down boxes to select a date from. The default alignment is:<br />
	 * 
	 * description | widget | quantifier
	 * 
	 * @param dbfieldName
	 *            the name of the field in the database that stores the info of
	 *            this widget
	 * @param tableName
	 *            the name of the database table where dbfieldName lives.
	 * @param description
	 *            the description label of this widget.
	 * @param quantifier
	 *            the quantifier label of this widget.
	 * @param error_msg
	 *            the error message to provide if validation is not passed
	 */
	public DropDownDateComponent(String dbfieldName, String tableName,
			String description, String quantifier, String error_msg) {
		initializeWidget(dbfieldName, tableName, description, quantifier,
				error_msg, true);
	}

	/**
	 * Creates drop down boxes to select a date from. The horizontal flag will
	 * dictate how the underlying components will be arranged. If horizontal is
	 * true, the arrangement is as follows:
	 * 
	 * description | widget | quantifier | error_msg
	 * 
	 * If horizontal is false, the arrangement is as follows:
	 * 
	 * description <br />
	 * widget <br />
	 * quantifier <br />
	 * error_msg
	 * 
	 * @param dbfieldName
	 *            the name of the field in the database that stores the info of
	 *            this widget.
	 * @param tableName
	 *            the name of the database table where dbfieldName lives.
	 * @param description
	 *            the description label of this widget.
	 * @param quantifier
	 *            the quantifier label of this widget.
	 * @param error_msg
	 *            the error message to provide if validation is not passed
	 * @param horizontal
	 *            flag to indicate whether horizontal or vertical.
	 */
	public DropDownDateComponent(String dbfieldName, String tableName,
			String description, String quantifier, String error_msg,
			boolean horizontal) {
		initializeWidget(dbfieldName, tableName, description, quantifier,
				error_msg, true);
	}

	/**
	 * Helper method in creating a new <code>DropDownDateComponent</code>.
	 * This allows for us to have one central method for creating a
	 * <code>DropDownDateComponent</code> even though we take in a different
	 * number of parameters.
	 * 
	 * @param dbfieldName
	 *            the name of the field in the database that stores the info of
	 *            this widget.
	 * @param tableName
	 *            the name of the database table where dbfieldName lives.
	 * @param description
	 *            the description label of this widget.
	 * @param quantifier
	 *            the quantifier label of this widget.
	 * @param error_msg
	 *            the error message to provide if validation is not passed
	 * @param horizontal
	 *            flag to indicate whether horizontal or vertical.
	 */
	protected void initializeWidget(String dbfieldName, String tableName,
			String description, String quantifier, String error_msg,
			boolean horizontal) {

		// all our widgets go to the horizontal panel
		HorizontalPanel datePanel = new HorizontalPanel();

		// creating the year dropdown. The year is by default set to 100 years
		// before the
		// current year and 50 years ahead of the current year.
		year = new ListBox();
		Date d = new Date();
		current_year = d.getYear() + 1900;
		for (int i = current_year - YEARS_BEFORE_CURRENT; i <= current_year
				+ YEARS_AHEAD_CURRENT; i++) {
			year.addItem(i + "");
		}
		year.addClickListener(new MyListener());

		// creating the month dropdown
		month = new ListBox();
		for (int i = 0; i < 12; i++) {
			month.addItem(months[i]);
		}
		month.addClickListener(new MyListener());

		// creating the days dropdown
		day = new ListBox();
		for (int i = 1; i < 32; i++) {
			day.addItem(i + "");
		}
		day.addClickListener(new MyListener());

		datePanel.add(year);
		datePanel.add(month);
		datePanel.add(day);

		// setting alts
		year.setTitle(description + " year");
		month.setTitle(description + " month");
		day.setTitle(description + " day");

		year.addFocusListener(this);
		month.addFocusListener(this);
		day.addFocusListener(this);

		year.setStyleName("drop_down");
		month.setStyleName("drop_down");
		day.setStyleName("drop_down");

		// initialize the widget.
		super.initialize(dbfieldName, tableName, description, quantifier,
				error_msg, datePanel, horizontal);

		// set the drop down date component to the current date.
		setDate(d.getYear() + 1900, d.getMonth() + 1, d.getDate());
	}

	/**
	 * Compiles a date string from the three widgets and returns it in the
	 * usually arraylist-hashmap format.
	 */
	public void sendAnswer(DataReceiver receiver) {
		String final_date = getText();

		Answer answer = new Answer();
		answer.add(tableName, dbfieldName, final_date);
		answer.add(hiddenFields);
		receiver.sendData(answer);
	}

	/**
	 * Compiles a date string from the three widgets and returns it in the
	 * usually arraylist-hashmap format.
	 */
	public Answer getAnswer() {
		String final_date = getText();

		Answer answer = new Answer();
		answer.add(tableName, dbfieldName, final_date);
		answer.add(hiddenFields);
		return answer;
	}

	/**
	 * When to focus is lost validate the field.
	 */
	public void onFocus(Widget sender) {
		String name = ((ListBox) sender).getStyleName();
		((ListBox) sender).setStyleName(name + "_focus");
	}

	/**
	 * When to focus is lost validate the field.
	 */
	public void onLostFocus(Widget sender) {

		String name = ((ListBox) sender).getStyleName();
		if (name.indexOf("_focus") != -1) {
			((ListBox) sender).setStyleName(name.substring(0, name.length()
					- "_focus".length()));
		}
	}

	/**
	 * Sets the widget to show a specific date.
	 * 
	 * @param y
	 *            the new year
	 * @param m
	 *            the new month
	 * @param d
	 *            the new day
	 */
	public void setDate(int y, int m, int d) {

		// get the maximum year
		int minimumYear = Integer.parseInt(year.getItemText(0));
		int diff = y - minimumYear;
		year.setSelectedIndex(diff);
		month.setSelectedIndex(m - 1);
		day.setSelectedIndex(d - 1);
	}

	/**
	 * Set's the widgets date given in the format xxxx-xx-xx
	 * 
	 * @param value
	 *            the date in the format xxxx-xx-xx
	 */
	private void setDate(String value) {
		if (value.length() == 10) {
			int year = Integer.parseInt(value.substring(0, 4));
			int month = Integer.parseInt(value.substring(5, 7));
			int day = Integer.parseInt(value.substring(8, 10));
			setDate(year, month, day);
		}

	}

	/**
	 * Sets the range of years that the user can select. The range starts from
	 * minimum (inclusive) and ends at maximum (inclusive).
	 * 
	 * @param minimum
	 *            the furthest year in the past the user can select.
	 * @param maximum
	 *            the furthest year in the future the user can select.
	 */
	public void setYearRange(int minimum, int maximum) {
		year.clear();
		Date d = new Date();
		current_year = d.getYear() + 1900;
		
		for (int i = minimum; i <= maximum; i++) {
			year.addItem(i + "");
		}	
		year.setSelectedIndex(maximum-minimum);
		year.addClickListener(new MyListener());
	}

	/**
	 * Populate this widget with FormData using an Answer object
	 * 
	 * @param answer
	 *            the answer object containing the data.
	 */
	public boolean populate(Answer answer) {

		if (answer == null)
			return false;

		// walking through all the fields-values of answer
		Iterator field_names = answer.getHashMap().keySet().iterator();
		while (field_names.hasNext()) {
			String field_name = (String) field_names.next();

			// if the db field name matches,
			if (field_name.equalsIgnoreCase(dbfieldName)) {
				String value = (String) answer.getHashMap().get(field_name);
				setDate(value);
				return true;
			}
		}

		return false;
	}

	public String getText() {
		String s_year = year.getValue(year.getSelectedIndex());
		String s_month = "" + (1 + month.getSelectedIndex());
		String s_day = day.getValue(day.getSelectedIndex());

		// inserting 0s in the front of months or days as needed
		if (s_month.length() == 1)
			s_month = "0" + s_month;
		if (s_day.length() == 1)
			s_day = "0" + s_day;

		String final_date = s_year + "-" + s_month + "-" + s_day;
		return final_date;
	}

	/**
	 * Populate this widget with FormData
	 */
	public boolean populate() {
		Answer a = FormData.getData(tableName);
		return populate(a);
	}

	public Object clone() {
		DropDownDateComponent d = new DropDownDateComponent(dbfieldName,
				description, error_msg);
		if (subquestion)
			d.setAsSubQuestion();
		return d;
	}

	/**
	 * Informs observers of change to this widget.
	 */
	private class MyListener implements ClickListener {
		public void onClick(Widget sender) {
			notifyObservers(DropDownDateComponent.this);
		}
	}
}
